package org.chen.security.config;

import com.fasterxml.jackson.databind.ObjectMapper;
import org.chen.common.RespUtils;
import org.chen.model.User;
import org.chen.respmsg.RespEnum;
import org.chen.vo.RespBean;
import org.chen.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.access.AccessDeniedException;
import org.springframework.security.authentication.AuthenticationCredentialsNotFoundException;
import org.springframework.security.authentication.BadCredentialsException;
import org.springframework.security.authentication.DisabledException;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.access.AccessDeniedHandler;
import org.springframework.security.web.authentication.AuthenticationFailureHandler;
import org.springframework.security.web.authentication.AuthenticationSuccessHandler;
import org.springframework.security.web.authentication.logout.LogoutSuccessHandler;
import org.springframework.util.DigestUtils;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;

@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    @Autowired
    UserService service;

    /**
     * 这个是用于身份认证的
     */
    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        //一定要passwordEncoder进行密码加密，否则报错,可以直接new BCryptPasswordEncoder进行security给予的加密
        //也可以自定义加密方式，自定义加密方式就是实现PasswordEncoder接口，实现里面的方法就好了
        auth.userDetailsService(service).passwordEncoder(new PasswordEncoder() {
            //加密
            @Override
            public String encode(CharSequence rawPassword) {
                //进行MD5加密，DigestUtils是spring提供的工具类
                return DigestUtils.md5DigestAsHex(rawPassword.toString().getBytes());
            }


            /**
             * 数据进行匹配对比
             * @param rawPassword 明文
             * @param encodedPassword 密文
             * @return
             */
            @Override
            public boolean matches(CharSequence rawPassword, String encodedPassword) {
                return encodedPassword.equals(DigestUtils.md5DigestAsHex(rawPassword.toString().getBytes()));
            }
        });
    }


    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http
                //验证请求
                .authorizeRequests()
                //admin/**的URL都需要有超级管理员角色，如果使用.hasAuthority()方法来配置，需要在参数中加上ROLE_,如下.hasAuthority("ROLE_超级管理员")
                .antMatchers("/hello").hasRole("测试角色3")
                //所有的请求
                .anyRequest()
                //都要验证
                .authenticated()
                .and()
                //表单登录的方式，formLogin()中配置了UsernamePasswordAuthenticationFilter和usernameParameter、passwordParameter
                .formLogin()
                //拦截后重定向的接口，默认的是会跳到一个页面，这里自定义跳转到/login_page接口，然后返回未登录的提示
                .loginPage("/login_page")
                //自定义登录的请求接口
                .loginProcessingUrl("/login")
                //自定义请求用户参数名
                .usernameParameter("username")
                //自定义请求密码参数名
                .passwordParameter("password")
                //登录成功后的操作（返回提示或者别的）
                .successHandler(new AuthenticationSuccessHandler() {
                    @Override
                    public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException, ServletException {
                        response.setContentType("application/json;charset=UTF-8");
                        User user = (User) authentication.getPrincipal();
                        RespBean<User> result = RespUtils.result(user, RespEnum.LOGIN_SUCCESS);
                        PrintWriter writer = response.getWriter();
                        String resp = new ObjectMapper().writeValueAsString(result);
                        writer.write(resp);
                        writer.flush();
                        writer.close();
                    }
                })
                //登录失败后的操作
                .failureHandler(new AuthenticationFailureHandler() {
                    @Override
                    public void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response, AuthenticationException exception) throws IOException, ServletException {
                        response.setContentType("application/json;charset=UTF-8");
                        RespBean<String> result = RespUtils.result(RespEnum.LOGIN_FAIL);
                        if (exception instanceof AuthenticationCredentialsNotFoundException){
                            result.setMessage("找不到用户密码");
                        }else if (exception instanceof UsernameNotFoundException){
                            result.setMessage("没有该用户");
                        }else if (exception instanceof BadCredentialsException){
                            result.setMessage("密码无效");
                        }else if (exception instanceof DisabledException){
                            result.setMessage("该用户不可用");
                        }
                        PrintWriter writer = response.getWriter();
                        String resp = new ObjectMapper().writeValueAsString(result);
                        writer.write(resp);
                        writer.flush();
                        writer.close();
                    }
                })
                //登录的请求可以通过
                .permitAll()
                .and()
                //注销
                .logout()
                .logoutSuccessHandler(new LogoutSuccessHandler() {
                    @Override
                    public void onLogoutSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException, ServletException {
                        response.setContentType("application/json;charset=UTF-8");
                        RespBean<String> result = RespUtils.result( RespEnum.LOGOUT_SUCCESS);
                        PrintWriter writer = response.getWriter();
                        String resp = new ObjectMapper().writeValueAsString(result);
                        writer.write(resp);
                        writer.flush();
                        writer.close();
                    }
                })
                .permitAll()
                .and()
                .csrf().disable()
                //允许配置异常处理
                .exceptionHandling()
                //访问拒绝异常处理
                .accessDeniedHandler(new AccessDeniedHandler() {
            @Override
            public void handle(HttpServletRequest request, HttpServletResponse response, AccessDeniedException accessDeniedException) throws IOException, ServletException {
                response.setContentType("application/json;charset=UTF-8");
                RespBean<String> result = RespUtils.result(RespEnum.ACCESS_DENIED);
                PrintWriter writer = response.getWriter();
                String resp = new ObjectMapper().writeValueAsString(result);
                writer.write(resp);
                writer.flush();
                writer.close();
            }
        });
    }
}
